home *** CD-ROM | disk | FTP | other *** search
/ QuickTime 2.0 Developer Kit / QuickTime 2.0 Developer Kit.iso / mac / MAC / Programming Stuff / Sample Code / Movie Toolbox / Burnt Text Sample Code / ConvertTextMovie.c < prev    next >
Encoding:
Text File  |  1994-12-09  |  14.9 KB  |  422 lines  |  [TEXT/KAHL]

  1. // ConvertMovie makes a copy of a movie, converting its text tracks to 
  2. // new text tracks with bit map representations of the text. The displayFlags
  3. // parameter allows one to add new display flags (such as normally time consuming
  4. // antialias and drop shadow) for the bit map images. The imageTrack parameter
  5. // indicates whether to save an image of the entire text track or just the
  6. // text box (as specified by the defaultTextBox field of the text descriptor).
  7. // The spatial parameter is a pointer to a compression information data structure
  8. // that specifies how to compress the text bit maps.
  9.  
  10. // Note that the resulting movie file (as specified by the dstSpec parameter) will
  11. // need to be flattened if it contained any non-text tracks in order to make it
  12. // self-contained.
  13.  
  14.  
  15. #include <QuickDraw.h>
  16. #include <Files.h>
  17. #include <StandardFile.h>
  18. #include <Movies.h>
  19. #include <ImageCompression.h>
  20. #include "ConvertTextMovie.h"
  21.  
  22. // Special Call to Text Media Handler
  23. pascal ComponentResult GetMetrics(MediaHandler mh, Handle metrics)
  24.  FIVEWORDINLINE(0x2F3C, 0x04, 0x108, 0x7000, 0xA82A);
  25.  
  26.  
  27. #define        FailNil(a)             if ((a)==nil)     goto bail;
  28. #define        FailOSErr(a)         if (err = a)     goto bail;
  29. #define        FailMemErr(a)        {a; if (err = MemError()) goto bail;}
  30. #define        FailMoviesErr(a)    {a; if (err = GetMoviesError()) goto bail;}
  31.  
  32. pascal OSErr ConvertAndAddTextTrack(Track srcTrack, Track dstTrack, long newDisplayFlags, Boolean imageTrack, SCSpatialSettings *spatial);
  33.  
  34. pascal void ConvertMovie(FSSpec *srcSpec, FSSpec *dstSpec, long newDisplayFlags, Boolean imageTrack, SCSpatialSettings *spatial)
  35. {
  36.     OSErr    err = 0;
  37.     Movie dstMovie = 0;
  38.     Movie srcMovie = 0;
  39.     Handle trackDataRef = 0;
  40.     Track srcTrack;
  41.     Track dstTrack;
  42.     Handle srcDataRef;
  43.     OSType srcDataRefType;
  44.     long srcDataRefAttributes;
  45.     short resRefNum = 0;
  46.     Fixed width,height;
  47.     OSType mediaType;
  48.     Media srcMedia;
  49.     Media dstMedia;
  50.     TimeValue selectionTime, selectionDuration;
  51.     GWorldPtr gw, movieWorld = 0;
  52.     GDHandle gd;
  53.     short dstRef = 0;
  54.     short srcRef;
  55.     short srcResID;
  56.     short dstResID = 0;
  57.     Rect bounds;
  58.     WindowPtr movieWindow = 0;
  59.     short numTracks, trackNum;
  60.     Point pt;
  61.     
  62.     GetGWorld(&gw, &gd);
  63.     
  64.     // Open the source movie file
  65.     FailOSErr( OpenMovieFile(srcSpec, &srcRef, fsRdPerm));
  66.     srcResID = 0;
  67.     FailOSErr( NewMovieFromFile(&srcMovie, srcRef, &srcResID, 0, 0, 0));
  68.     CloseMovieFile(srcRef);
  69.     srcRef = 0;
  70.     
  71.     // Create a new movie file for the destination
  72.     CreateMovieFile(dstSpec, 'TVOD', 0, createMovieFileDeleteCurFile, &dstRef, &dstMovie);
  73.     AddMovieResource(dstMovie, dstRef, &dstResID, 0);
  74.     
  75.     GetMovieBox(srcMovie, &bounds);
  76.     OffsetRect(&bounds, -bounds.left, -bounds.top);
  77.     SetMovieBox(srcMovie, &bounds);
  78.     
  79.     // Image the movie into an offscreen buffer
  80.     FailOSErr( NewGWorld(&movieWorld, spatial->depth, &bounds, 0L, 0L, useTempMem));
  81.     SetMovieGWorld(srcMovie, movieWorld, 0);
  82.     
  83.     OffsetRect(&bounds, 100, 100);
  84.     // Create a new window to which we can copy the movie image during the conversion
  85.     movieWindow = NewCWindow(0, &bounds, (StringPtr) "\pConverting...", true, noGrowDocProc, (WindowPtr)-1, false, 0);
  86.     AlignWindow(movieWindow, false, &bounds, nil);
  87.     SetMovieActive(srcMovie, true);
  88.     
  89.     SetPort((GrafPtr)movieWindow);
  90.     
  91.     // Iterate through the tracks, copying non-text tracks and converting text tracks
  92.     numTracks = GetMovieTrackCount(srcMovie);
  93.     for (trackNum = 1; trackNum <= numTracks; trackNum++) {
  94.         ClearMoviesStickyError();
  95.         srcTrack =  GetMovieIndTrack(srcMovie, trackNum);
  96.         srcMedia = GetTrackMedia(srcTrack);
  97.         
  98.         srcDataRef = nil;
  99.             
  100.         GetTrackDimensions(srcTrack, &width, &height);
  101.         dstTrack = NewMovieTrack(dstMovie, width, height, GetTrackVolume(srcTrack));
  102.     
  103.         GetMediaHandlerDescription(srcMedia, &mediaType, 0L, 0L);
  104.         
  105.         if (mediaType == 'text') {
  106.             // TEXT TRACK; CONVERT IT TO BITMAP ("BURNT TEXT") REPRESENTATION
  107.             if (err = ConvertAndAddTextTrack(srcTrack, dstTrack, newDisplayFlags, imageTrack, spatial)) {
  108.                 //DebugStr((StringPtr) "\pError returned by ConvertAndAddTextTrack");
  109.                 goto bail;
  110.             }
  111.         } else {
  112.             // NON-TEXT TRACK; COPY A REFERENCE
  113.             // Obtain the data ref of the source media since we are making a reference copy of the track.
  114.             // The user will need to flatten the resulting file if they want a stand alone movie file.
  115.             err = GetMediaDataRef(srcMedia, 1, &srcDataRef, &srcDataRefType, &srcDataRefAttributes);
  116.             dstMedia = NewTrackMedia( dstTrack, mediaType,
  117.                         GetMediaTimeScale(srcMedia), srcDataRef, srcDataRefType);
  118.             
  119.             selectionTime = 0;
  120.             selectionDuration = GetMovieDuration(srcMovie);
  121.                 
  122.             InsertTrackSegment(srcTrack, dstTrack, selectionTime, selectionDuration, 0);
  123.             CopyTrackSettings(srcTrack, dstTrack);
  124.                 
  125.             DisposeHandle(srcDataRef);
  126.             srcDataRef = 0;
  127.         }
  128.         if (err = GetMoviesStickyError()) goto bail;
  129.     }
  130.  
  131.     UpdateMovieResource(dstMovie, dstRef, dstResID, 0);
  132.     err = GetMoviesError();
  133.  
  134. bail:
  135.     // Clean up
  136.     SetGWorld(gw, gd);
  137.     if (movieWorld) DisposeGWorld(movieWorld);
  138.     if (srcRef) CloseMovieFile(srcRef);
  139.     if (dstRef) CloseMovieFile(dstRef);
  140.     if (srcMovie) DisposeMovie(srcMovie);
  141.     if (dstMovie) DisposeMovie(dstMovie);
  142.     if (movieWindow) DisposeWindow(movieWindow);
  143. }
  144.  
  145. #define min(x,y) ((x) < (y)) ? (x) : (y)
  146. #define dfDontHilite (1L << 30)
  147. #define dfDontBitmap (1L << 29)
  148.  
  149. pascal OSErr ConvertAndAddTextTrack(Track srcTrack, Track dstTrack, long newDisplayFlags, Boolean imageTrack, SCSpatialSettings *spatial)
  150. {
  151.     OSErr err;
  152.     Movie srcMovie = GetTrackMovie(srcTrack);
  153.     Media textMedia;
  154.     MediaHandler textMediaHandler;
  155.     Media newTextMedia;
  156.     short sampleFlags;
  157.     Fixed width, height;
  158.     RgnHandle trackRgn;
  159.     long size, dataSize, newDataSize;
  160.     TimeValue sampleTime, sampleDuration, thisTrackTime, nextTrackTime, interestingDuration, mediaTime;
  161.     ImageDescriptionHandle idh = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription));
  162.     Handle dataOut = NewHandle(4);
  163.     TextDescriptionHandle tdh = (TextDescriptionHandle) NewHandleClear(sizeof(TextDescription));
  164.     Ptr imageData = 0;
  165.     Ptr descPtr;
  166.     Ptr dataPtr;
  167.     long    descIndex;
  168.     Handle metrics = NewHandle(0);
  169.     GWorldPtr movieGW;
  170.     GDHandle movieGD;
  171.     GWorldPtr saveGW;
  172.     GDHandle saveGD;
  173.     Rect trackRect;
  174.     Rect textBox;
  175.     long compressedDataSize;
  176.     Rect    movieBox;
  177.     WindowPtr movieWindow = 0;
  178.     MatrixRecord trackMatrix, movieMatrix;
  179.     long textLen;
  180.     
  181.     GetPort((GrafPtr*)&movieWindow);
  182.     GetMovieBox(srcMovie, &movieBox);
  183.  
  184.     // Create new text media
  185.     textMedia = GetTrackMedia(srcTrack);
  186.     textMediaHandler = GetMediaHandler(textMedia);
  187.     FailMoviesErr( newTextMedia = NewTrackMedia(dstTrack, 'text', GetMediaTimeScale(textMedia), 0L, 0L));
  188.  
  189.     // Copy info from source track
  190.     err = CopyTrackSettings(srcTrack, dstTrack);
  191.     FailMoviesErr( SetTrackLayer(dstTrack, GetTrackLayer(srcTrack)));
  192.     
  193.     // Find the first text sample
  194.     thisTrackTime = GetTrackOffset(srcTrack);
  195.     GetTrackNextInterestingTime(srcTrack, nextTimeMediaSample+nextTimeEdgeOK, thisTrackTime, kFix1, 
  196.                 &nextTrackTime, &interestingDuration);
  197.     SetMovieTimeValue(srcMovie, thisTrackTime);
  198.     
  199.     // Get the track bounds and set up for compressing text image
  200.     trackRgn = GetTrackSegmentDisplayBoundsRgn(srcTrack, 0, GetTrackDuration(srcTrack));
  201.     if (!trackRgn || EmptyRgn(trackRgn)) {
  202.         err = -1;
  203.         goto bail;
  204.     }
  205.     trackRect = (*trackRgn)->rgnBBox;
  206.     GetMovieGWorld(srcMovie, &movieGW, &movieGD);
  207.     FailOSErr( GetMaxCompressionSize(movieGW->portPixMap, &trackRect, spatial->depth, spatial->spatialQuality, 
  208.                 spatial->codecType, 0, &size));
  209.     FailMemErr( imageData = NewPtr(size));
  210.     
  211.     // Get track matrix so that when we compress the image we are looking 
  212.     // in the right place in the offscreen buffer
  213.     GetMovieMatrix(srcMovie, &movieMatrix);
  214.     GetTrackMatrix(srcTrack, &trackMatrix);
  215.     ConcatMatrix(&movieMatrix, &trackMatrix);
  216.  
  217.     FailOSErr( BeginMediaEdits(newTextMedia));
  218.     while (thisTrackTime >= 0) {
  219.         TimeValue dur;
  220.         short addFlags = 0;
  221.         // Get the current sample
  222.         mediaTime = TrackTimeToMediaTime(thisTrackTime, srcTrack);
  223.         FailOSErr( GetMediaSample(textMedia, dataOut, 0, &dataSize, mediaTime, &sampleTime, &sampleDuration, 
  224.                 (SampleDescriptionHandle) tdh, &descIndex, 0, 0, &sampleFlags));
  225.         
  226.         // Add in the new display flags (and set special dontHilite & dontBitmap flags)
  227.         (*tdh)->displayFlags |= (newDisplayFlags + dfDontHilite + dfDontBitmap);
  228.         err = SetMediaSampleDescription(textMedia, descIndex, (SampleDescriptionHandle) tdh);
  229.         
  230.         SetMovieTimeValue(srcMovie, thisTrackTime);
  231.  
  232.         newDataSize = dataSize;
  233.  
  234.         // Get the track bounds for this time; if imageTrack is true we will compress the entire track image
  235.         // otherwise we just image that portion of the track specified by the defaultTextBox field
  236.         DisposeRgn(trackRgn);
  237.         trackRgn = GetTrackDisplayBoundsRgn(srcTrack); // rgn could change over time
  238.         trackRect = (*trackRgn)->rgnBBox;
  239.         if (imageTrack) {
  240.             textBox = trackRect;
  241.             OffsetRect(&textBox, -trackRect.left, -trackRect.top);
  242.             (*tdh)->defaultTextBox = textBox;
  243.         } else {
  244.             textBox = (*tdh)->defaultTextBox;
  245.             // fix up textBox so it doesn't extend beyond the track
  246.             if (textBox.right > trackRect.right-trackRect.left)
  247.                 textBox.right = trackRect.right-trackRect.left;
  248.             if (textBox.bottom > trackRect.bottom-trackRect.top)
  249.                 textBox.bottom = trackRect.bottom-trackRect.top;
  250.             (*tdh)->defaultTextBox = textBox;
  251.         }
  252.  
  253.         // Non-sync samples are hilite samples; we don't need to convert them
  254.         if (sampleFlags & mediaSampleNotSync) {
  255.             addFlags = mediaSampleNotSync;
  256.             goto skipConversion;
  257.         }
  258.  
  259.         // Force update
  260.         MoviesTask(srcMovie, 0);
  261.         MoviesTask(srcMovie, 0);
  262.         
  263.         // Copy bits to window (so the user sees something happening)
  264.         CopyBits((BitMap*)&movieGW->portPixMap, (BitMap*)&movieWindow->portBits, &movieBox, &movieBox, srcCopy, 0);
  265.         
  266.         // Get text metrics data structure for current time (used by text media handler for hiliting)
  267.         // This will be added to the text sample below
  268.         FailOSErr( GetMetrics(textMediaHandler, metrics));
  269.  
  270.         // Use track matrix to find text box's location within the movie box
  271.         TransformRect(&trackMatrix, &textBox, 0);
  272.         // Safety code: Force text box to be within the bounds of the track
  273.         textBox.right = min(textBox.right, trackRect.right);
  274.         textBox.bottom = min(textBox.bottom, trackRect.bottom);
  275.         // More safety code: Don't allow text box height to be too small 
  276.         if ((textBox.bottom - textBox.top) <= 4)
  277.             textBox.bottom = trackRect.bottom;
  278.         
  279.         // Compress the text image
  280.         GetGWorld(&saveGW, &saveGD);
  281.         SetGWorld(movieGW, movieGD);
  282.         ForeColor(blackColor);
  283.         BackColor(whiteColor);
  284.         FailOSErr( FCompressImage(movieGW->portPixMap, &textBox, spatial->depth, spatial->spatialQuality, spatial->codecType, 0, 0, 0, 0, 0, 0, idh, imageData));
  285.         SetGWorld(saveGW, saveGD);
  286.                 
  287.         /********************************************************************/
  288.         /*  Build the new text descriptor                                    */
  289.         /********************************************************************/
  290.         
  291.         // If defaultFontName isn't there then add one
  292.         if ((long)&(*tdh)->defaultFontName - (long)(*tdh) >= (*tdh)->size) {
  293.             short fontNumber = (*tdh)->defaultStyle.scrpFont;
  294.             Str255 fontName;
  295.             fontName[0] = 0;
  296.  
  297.             // fontNum of 0 or 1 is special case (sys or app font) -> Don't use name
  298.             if (fontNumber != 0 && fontNumber != 1)
  299.                 GetFontName(fontNumber, fontName);
  300.                 
  301.             FailMemErr( SetHandleSize((Handle)tdh, sizeof(TextDescription) + fontName[0]));
  302.             BlockMove(fontName, (*tdh)->defaultFontName, fontName[0]+1);
  303.             
  304.             // Don't use sizeof(TextDescription) to set size, since it pads the length to an even byte
  305.             (*tdh)->size = ((long)&(*tdh)->defaultFontName - (long)(*tdh) + 1) + fontName[0];
  306.         }
  307.  
  308.         // increase text description handle size to account for size of the image descriptor + atom header bytes (8)
  309.         FailMemErr( SetHandleSize((Handle)tdh, (*tdh)->size + (*idh)->idSize + 8));
  310.         (*tdh)->size = (*tdh)->size + (*idh)->idSize + 8;
  311.  
  312.         HLock((Handle)tdh);
  313.  
  314.         // Point past the default font name
  315.         descPtr = (Ptr)((long)&(*tdh)->defaultFontName + (*tdh)->defaultFontName[0] + 1);
  316.  
  317.         // image descriptor atom header
  318.         *(long*)descPtr = (*idh)->idSize + 8;
  319.         descPtr += 4;
  320.         *(long*)descPtr = 'idsc';
  321.         descPtr += 4;
  322.  
  323.         // copy image descriptor
  324.         compressedDataSize = (*idh)->dataSize;
  325.         (*idh)->dataSize = 0; // set to zero so that all image descs will be same (field is not used at decompress)
  326.         BlockMove(*idh, descPtr, (*idh)->idSize);
  327.         
  328.         HUnlock((Handle)tdh);
  329.  
  330.         /********************************************************************/
  331.         /*  Build the new text sample                                        */
  332.         /********************************************************************/
  333.         
  334.         // Look for and delete any existing bitmap or metric data from existing sample
  335.         textLen = *((short*) *dataOut);
  336.         if (dataSize > (textLen + 2)) {
  337.             // We got extra stuff
  338.             Ptr        dataPtr = (*dataOut) + textLen + 2;
  339.             long    dataLen;
  340.             
  341.             do    {
  342.                 dataLen = *((long*) dataPtr);
  343.                 if (dataLen <= 0 || dataLen > dataSize) break; // safety check
  344.                 
  345.                 if (((*((long*) (dataPtr+4))) == 'imag') || ((*((long*) (dataPtr+4))) == 'metr')) {
  346.                     // Delete the atom by sliding the remaining data over to fill in the gap
  347.                     long restOfDataSize = dataSize - (dataPtr+dataLen - *dataOut);
  348.                     if (restOfDataSize > 0)
  349.                         BlockMove(dataPtr+dataLen, dataPtr, restOfDataSize);
  350.                     dataSize -= dataLen;
  351.                 } else
  352.                     dataPtr += dataLen;
  353.             } while ((dataPtr - *dataOut) < dataSize);
  354.         }
  355.     
  356.         // Add Compressed Image to SampleData
  357.         newDataSize = dataSize + compressedDataSize + 16 + GetHandleSize(metrics); // 16 = size of 2 atom headers
  358.         
  359.         FailMemErr( SetHandleSize(dataOut, newDataSize));
  360.  
  361.         HLock(dataOut);
  362.         dataPtr = *dataOut+dataSize;
  363.         
  364.         // 'imag' atom header
  365.         *(long*)dataPtr = compressedDataSize + 16;
  366.         dataPtr += 4;
  367.         *(long*)dataPtr = 'imag';
  368.         dataPtr += 4;
  369.         
  370.         // image data atom header
  371.         *(long*)dataPtr = compressedDataSize + 8;
  372.         dataPtr += 4;
  373.         *(long*)dataPtr = 'idat';
  374.         dataPtr += 4;
  375.         
  376.         // copy image data
  377.         BlockMove(imageData, dataPtr, compressedDataSize);
  378.         
  379.         // copy text metrics
  380.         BlockMove(*metrics, dataPtr+compressedDataSize, GetHandleSize(metrics));
  381.         
  382.         HUnlock(dataOut);
  383.  
  384. skipConversion:
  385.         // Turn off the don't hilite flag
  386.         (*tdh)->displayFlags &= ~dfDontHilite;
  387.         (*tdh)->displayFlags &= ~dfDontBitmap;
  388.         
  389.         // Add the new sample data to new media
  390.         FailOSErr( AddMediaSample(newTextMedia, dataOut, 0, newDataSize, sampleDuration,
  391.                 (SampleDescriptionHandle) tdh, 1, addFlags, &sampleTime));
  392.         
  393.         // Insert media into same location (thisTrackTime) in new track
  394.         if (interestingDuration < 0 || interestingDuration > sampleDuration) interestingDuration = sampleDuration;
  395.         dur = GetTrackDuration(dstTrack);
  396.         FailOSErr( InsertMediaIntoTrack(dstTrack, thisTrackTime, sampleTime, interestingDuration, kFix1));
  397.         dur = GetTrackDuration(dstTrack);
  398.             
  399.         // Get time for next text sample
  400.         GetTrackNextInterestingTime(srcTrack, nextTimeMediaSample, thisTrackTime, kFix1, &nextTrackTime, &interestingDuration);
  401.         if (thisTrackTime == nextTrackTime) {
  402.             //DebugStr((StringPtr)"\pGetTrackNextInterestingTime returned same time");
  403.             goto bail;
  404.         }
  405.         thisTrackTime = nextTrackTime;
  406.     }
  407.     err = EndMediaEdits(newTextMedia);
  408.     
  409.     
  410. bail:
  411.     DisposeHandle(dataOut);
  412.     DisposeHandle((Handle)idh);
  413.     DisposeHandle((Handle)tdh);
  414.     DisposeHandle(metrics);
  415.     if (imageData) DisposePtr(imageData);
  416.     if (trackRgn) DisposeRgn(trackRgn);
  417.     
  418.     return err;
  419.     
  420. }
  421.  
  422.